/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#ifndef SCHEDULE_H
#define SCHEDULE_H

#include <plat/config.h>
#include <arch/cpu.h>
#include <stdlib/stdint.h>
#include <core/task.h>

#define ULONG_BITS          (sizeof(unsigned long) * 8)
#define ULONG_LEFT_MOST_BIT (1UL << (ULONG_BITS - 1))

#define ACTION_RUN_CURRENT ((tcb_t *)0)
#define ACTION_CHOOSE_NEW  ((tcb_t *)1)
#define ACTION_RUN_IDLE    ((tcb_t *)2)

typedef struct schedule_data {
    spinlock_t lock;
    tcb_t     *pcurrent;
    tcb_t     *pidle;

    list_t readyqueue[CONFIG_MAX_PRIORITY * CONFIG_DOM_NUMBER];
    list_t pendqueue;

    tcb_t *action;

    unsigned long lvl1bit[CONFIG_DOM_NUMBER];
    unsigned long lvl2bit[((CONFIG_MAX_PRIORITY + ULONG_BITS - 1) / ULONG_BITS) * CONFIG_DOM_NUMBER];

#ifdef CONFIG_KERNEL_MCS
    list_t        critical_queue;
    unsigned char sys_critical_level;
#endif
} schedule_data_t;

static inline tcb_t *current(void)
{
    extern schedule_data_t sd[];

#ifdef CONFIG_ENABLE_SMP
    return sd[current_cpu()].pcurrent;
#else
    return sd[0].pcurrent;
#endif
}

static inline schedule_data_t *get_schedule_data(unsigned int core_id)
{
    extern schedule_data_t sd[];
    assert(core_id < CONFIG_SMP_CORES);

    return sd + core_id;
}

static inline schedule_data_t *current_schedule_data(void)
{
    extern schedule_data_t sd[];
#ifdef CONFIG_ENABLE_SMP
    return sd + current_cpu();
#else
    return sd;
#endif
}

static inline int bitmap_is_empty(const schedule_data_t *sd, dom_t dom)
{
    assert(dom < domScheduleLength);

    return sd->lvl1bit[dom] == 0;
}

static inline void bitmap_set(schedule_data_t *sd, dom_t dom, unsigned long prio)
{
    unsigned int l1index;
    unsigned int l2index;

    assert(dom < domScheduleLength);
    assert(prio < sizeof(sd->lvl2bit[0]) * 8);

    l1index = prio / ULONG_BITS;
    sd->lvl1bit[dom] |= ULONG_LEFT_MOST_BIT >> l1index;

    l1index += ((CONFIG_MAX_PRIORITY + ULONG_BITS - 1) / ULONG_BITS) * dom;
    l2index = prio % ULONG_BITS;
    sd->lvl2bit[l1index] |= ULONG_LEFT_MOST_BIT >> l2index;
}

static inline int readyqueue_is_empty(const schedule_data_t *sd, dom_t dom)
{
    assert(dom < domScheduleLength);

    return bitmap_is_empty(sd, dom);
}
static inline int pendqueue_is_empty(schedule_data_t *sd)
{
    return list_is_empty(&sd->pendqueue);
}
void schedule_run(tcb_t *task);

void schedule_attach(tcb_t *task);

void schedule_detach(tcb_t *task);

void schedule_wakeup(unsigned int currenttime);

void schedule(void);

void changeToNextDomain(void);

void schedule_init(tcb_t *idle);

void schedule_accept_task(tcb_t *task);

void schedule_action_change(tcb_t *action);

void task_enque(schedule_data_t *sd, tcb_t *task);

#ifdef CONFIG_KERNEL_MCS
long                 check_critical_point(schedule_data_t *sd);
void                 criticalqueue_enque(schedule_data_t *sd, tcb_t *tcb);
void                 criticalqueue_deque(tcb_t *tcb);
void                 rescheduleRequired(void);
void                 mcs_pendqueue_enque(schedule_data_t *sd, tcb_t *tcb);
static inline tcb_t *get_critical_list_firsttask(schedule_data_t *sd)
{
    return container_of(sd->critical_queue.prev, tcb_t, critial_list);
}
#endif
#endif
